package com.lxw.multidb.modules.file;

import lombok.Data;
import lombok.experimental.Accessors;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * 目录树构建配置选项
 * <p>
 * 配置分类：
 * 1. 过滤选项：类型过滤、扩展名过滤、名称过滤、隐藏文件
 * 2. 排序选项：按名称/大小/时间排序（三选一）、升降序、目录优先
 * <p>
 * 使用示例：
 * <pre>
 * // 默认配置
 * TreeOptions options = TreeOptions.defaults();
 *
 * // 只显示PDF文件
 * TreeOptions options = TreeOptions.filesOnly().includeExtensions("pdf");
 *
 * // 排除临时文件，按大小降序
 * TreeOptions options = TreeOptions.defaults()
 *     .excludeExtensions("tmp", "log")
 *     .setSortBySize(true)
 *     .setDescending(true);
 *
 * // 搜索名称包含"report"的文件
 * TreeOptions options = TreeOptions.defaults()
 *     .setNameFilter("report")
 *     .setCaseSensitive(false);
 * </pre>
 *
 * @author lxw
 * @version 3.0
 * @date 2025-11-06
 */
@Data
@Accessors(chain = true)
public class TreeOptions {

    // ==================== 过滤选项 ====================

    /**
     * 只显示目录（不显示文件）
     * <p>
     * 用途：构建纯目录结构，用于目录选择器等场景
     * <p>
     * 默认值：false
     * 冲突：不能与filesOnly同时为true
     */
    private boolean directoriesOnly = false;

    /**
     * 只显示文件（不显示目录）
     * <p>
     * 用途：文件列表展示，隐藏目录结构
     * <p>
     * 默认值：false
     * 冲突：不能与directoriesOnly同时为true
     */
    private boolean filesOnly = false;

    /**
     * 是否包含隐藏文件/目录（以"."开头的项）
     * <p>
     * 说明：
     * - true：显示所有文件，包括.git、.DS_Store等隐藏项
     * - false：过滤掉以"."开头的文件和目录
     * <p>
     * 默认值：false
     * 适用场景：开发工具、系统管理工具
     */
    private boolean includeHiddenFiles = false;

    /**
     * 包含的文件扩展名白名单
     * <p>
     * 行为：
     * - null或空：不限制，显示所有扩展名
     * - 非空：只显示指定扩展名的文件
     * <p>
     * 注意：
     * - 扩展名不含点，如"pdf"、"jpg"
     * - 扩展名统一转为小写后比较
     * - 只对文件生效，不影响目录
     * <p>
     * 示例：
     * <pre>
     * options.includeExtensions("pdf", "docx", "xlsx");  // 只显示Office文档
     * options.includeExtensions("jpg", "png", "gif");    // 只显示图片
     * </pre>
     */
    private Set<String> includeExtensions;

    /**
     * 排除的文件扩展名黑名单
     * <p>
     * 行为：
     * - null或空：不排除
     * - 非空：排除指定扩展名的文件
     * <p>
     * 优先级：
     * - 如果同时设置了includeExtensions和excludeExtensions
     * - 先检查includeExtensions（白名单）
     * - 再检查excludeExtensions（黑名单）
     * <p>
     * 示例：
     * <pre>
     * options.excludeExtensions("tmp", "log", "bak");  // 排除临时文件
     * </pre>
     */
    private Set<String> excludeExtensions;

    /**
     * 文件/目录名称过滤关键词
     * <p>
     * 行为：
     * - null或空：不过滤
     * - 非空：只显示名称中包含此关键词的项（使用contains匹配）
     * <p>
     * 配合：
     * - caseSensitive：控制是否区分大小写
     * <p>
     * 应用场景：
     * - 搜索功能
     * - 快速定位文件
     * <p>
     * 示例：
     * <pre>
     * options.setNameFilter("report").setCaseSensitive(false);  // 搜索"report"（不区分大小写）
     * </pre>
     */
    private String nameFilter;

    /**
     * 名称过滤是否区分大小写
     * <p>
     * 仅当nameFilter不为空时生效
     * <p>
     * 默认值：false（不区分大小写，更友好）
     */
    private boolean caseSensitive = false;

    // ==================== 排序选项 ====================

    /**
     * 按名称排序
     * <p>
     * 排序规则：字典序，不区分大小写
     * <p>
     * 默认值：false
     * 冲突：与sortBySize、sortByModifiedTime互斥（只能选一个）
     */
    private boolean sortByName = false;

    /**
     * 按文件大小排序
     * <p>
     * 排序规则：从小到大（ascending）或从大到小（descending）
     * <p>
     * 默认值：false
     * 冲突：与sortByName、sortByModifiedTime互斥
     * 注意：目录的大小为0
     */
    private boolean sortBySize = false;

    /**
     * 按修改时间排序
     * <p>
     * 排序规则：从旧到新（ascending）或从新到旧（descending）
     * <p>
     * 默认值：false
     * 冲突：与sortByName、sortBySize互斥
     * 注意：如果某些对象没有修改时间，会排在最后
     */
    private boolean sortByModifiedTime = false;

    /**
     * 目录优先排序
     * <p>
     * 行为：
     * - true：目录排在文件前面
     * - false：目录和文件混合排序
     * <p>
     * 优先级：高于其他排序规则
     * <p>
     * 默认值：true（符合常见文件管理器的行为）
     */
    private boolean directoriesFirst = true;

    /**
     * 倒序排序（降序）
     * <p>
     * 配合其他排序选项使用：
     * - sortByName=true, descending=true → Z-A排序
     * - sortBySize=true, descending=true → 从大到小排序
     * - sortByModifiedTime=true, descending=true → 从新到旧排序
     * <p>
     * 默认值：false（升序）
     */
    private boolean descending = false;

    // ==================== 静态工厂方法 ====================

    /**
     * 创建默认配置
     * <p>
     * 默认行为：
     * - 显示所有文件和目录（不过滤）
     * - 不显示隐藏文件
     * - 按名称升序排序
     * - 目录优先排序
     * <p>
     * 适用场景：通用文件浏览器
     *
     * @return 默认配置对象
     */
    public static TreeOptions defaults() {
        return new TreeOptions()
                .setSortByName(true)
                .setDirectoriesFirst(true);
    }

    /**
     * 创建只显示目录的配置
     */
    public static TreeOptions directoriesOnly() {
        return new TreeOptions()
                .setDirectoriesOnly(true)
                .setSortByName(true)
                .setDirectoriesFirst(true);
    }

    /**
     * 创建只显示文件的配置
     */
    public static TreeOptions filesOnly() {
        return new TreeOptions()
                .setFilesOnly(true)
                .setSortByName(true);
    }

    // ==================== 便捷方法 ====================

    /**
     * 设置包含的文件扩展名（可变参数）
     * <p>
     * 示例：options.includeExtensions("pdf", "docx", "txt")
     */
    public TreeOptions includeExtensions(String... extensions) {
        if (extensions != null && extensions.length > 0) {
            this.includeExtensions = new HashSet<>(Arrays.asList(extensions));
        }
        return this;
    }

    /**
     * 设置排除的文件扩展名（可变参数）
     * <p>
     * 示例：options.excludeExtensions("tmp", "log", "bak")
     */
    public TreeOptions excludeExtensions(String... extensions) {
        if (extensions != null && extensions.length > 0) {
            this.excludeExtensions = new HashSet<>(Arrays.asList(extensions));
        }
        return this;
    }

    // ==================== 验证方法 ====================

    /**
     * 验证配置的有效性
     * <p>
     * 验证规则：
     * 1. directoriesOnly和filesOnly不能同时为true
     * 2. 排序方式只能选择一种（sortByName/sortBySize/sortByModifiedTime）
     * <p>
     * 调用时机：在使用配置之前，通常在loadChildren方法开始时调用
     *
     * @throws IllegalArgumentException 配置无效时抛出异常，包含详细错误信息
     */
    public void validate() {
        // 检查互斥条件
        if (directoriesOnly && filesOnly) {
            throw new IllegalArgumentException(
                    "配置冲突：directoriesOnly 和 filesOnly 不能同时为 true");
        }

        // 检查排序选项（只能选择一种排序方式）
        int sortCount = 0;
        if (sortByName) sortCount++;
        if (sortBySize) sortCount++;
        if (sortByModifiedTime) sortCount++;

        if (sortCount > 1) {
            throw new IllegalArgumentException(
                    "配置冲突：只能选择一种排序方式（sortByName/sortBySize/sortByModifiedTime）");
        }
    }

    /**
     * 生成配置的可读字符串表示
     * <p>
     * 只显示已设置的选项，方便调试和日志记录
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("TreeOptions{");

        // 过滤选项
        if (directoriesOnly) sb.append("directoriesOnly, ");
        if (filesOnly) sb.append("filesOnly, ");
        if (includeHiddenFiles) sb.append("includeHidden, ");

        if (includeExtensions != null && !includeExtensions.isEmpty()) {
            sb.append("include=").append(includeExtensions).append(", ");
        }
        if (excludeExtensions != null && !excludeExtensions.isEmpty()) {
            sb.append("exclude=").append(excludeExtensions).append(", ");
        }
        if (nameFilter != null && !nameFilter.isEmpty()) {
            sb.append("nameFilter='").append(nameFilter).append("', ");
        }

        // 排序选项
        if (sortByName) sb.append("sortByName, ");
        if (sortBySize) sb.append("sortBySize, ");
        if (sortByModifiedTime) sb.append("sortByTime, ");
        if (directoriesFirst) sb.append("dirFirst, ");
        if (descending) sb.append("desc, ");

        // 移除最后的逗号和空格
        if (sb.length() > 12) {
            sb.setLength(sb.length() - 2);
        }
        sb.append("}");

        return sb.toString();
    }
}